#!/bin/bash
#========================================================================
# Author: YSLin
# Email: 
# File Name: array.sh
# Description: 
#   -declaration/access of array
#   -uninitialized array variable
#   -number of array
#   -traverse/show element of array
#   -various methods of access/handle element of array
#   -string operation for array
#   -declare/read -a array (宣告/使用者輸入陣列)
#   -add element/pop element/copy array
#   -sort array
#   -compare two array to find the same/the diff
#   -emulate array by variable
#   IFS : The  Internal  Field  Separator. 可以將array分隔符號轉變, read的split符號
# Edit History: 
#   2010-11-22    File created.
#========================================================================

#========================================================================
#   -declaration/access of array
#========================================================================
# Yet another way of assigning array variables...
# array_name=([xx]=XXX [yy]=YYY ...)

decl1=([17]=seventeen [24]=twenty-four)

echo -n "decl1[17] = "
echo ${decl1[17]}

echo -n "decl1[24] = "
echo ${decl1[24]}

decl2=(first "second" 'third with space' fourth\ with\ space)

IFS=$'\n' #避免空白問題
#for loop存取array內容,會有空白的問題
for name in ${decl2[@]}; do
	echo $name
done

# 顯示各要素,不會有空白地問題
for n in 0 1 2 3; do
    echo "${decl2[$n]}"
done                   
#========================================================================
#   -uninitialized array variable
#========================================================================
# Contents of uninitialized array variable print blank (null variable).
echo -n "uninit[1] = "
echo ${uninit[1]}
echo "(uninit[1] unassigned)"

uninit[0]=10
uninit[1]=20
# Sum of two array variables assigned to third
uninit[2]=`expr ${uninit[0]} + ${uninit[1]}`
echo "uninit[2] = uninit[0] + uninit[1]"
echo -n "uninit[2] = "
echo ${uninit[2]}


uninit[2]=10
uninit[3]=xx
# This fails because adding an integer to a string is not permitted.
uninit[4]=`expr ${uninit[2]} + ${uninit[3]}`
echo "uninit[4] = uninit[2] + uninit[3]"
echo -n "uninit[4] = "
echo ${uninit[4]}

#========================================================================
#   -number of array
#========================================================================
num=(first second third)
#${#array[@]} 代表所有array總數
#${array[0]} 是array的第一個變數值, 也就是從0開始, 最大值-1也就是最後一個值
for ((i=0; i<${#num[@]}; i++)); do    #請注意 ((   )) 雙層括號
	echo "$i: ${num[$i]}"
done
#顯示所有array內容
IFS=,
echo "num: {${num[*]}}"

# 編號有連續的情況
sample1[0]="zero"
sample1[1]="one"
sample1[2]="two"
echo "連號:${#sample1[*]}" # 3

# 編號不連續的情況                                                                                                                            
sample2[1]="first"
sample2[7]="second"
sample2[10]="third"
echo "不連號:${#sample2[*]}" # 3

#========================================================================
#   -traverse/show element of array
#========================================================================
traverse=(peter cokula gintama shinbagi)
#for loop存取array內容
for name in ${traverse[@]}; do
	echo "name:$name"
done
								
#for loop with index i
for ((i=0; i<${#traverse[@]}; i++)); do    #請注意 ((   )) 雙層括號
	echo "traverse[$i]: ${traverse[$i]}"
done
# 將所有的變數用「,」連起來並顯示
IFS=,
echo "traverse: {${traverse[*]}}"

# 將所有的要數用換行文字連起來並顯示(=每一行顯示一個要素)
IFS=$'\n'  # 換行文字
echo "${traverse[*]}"


#Bash permits array operations on variables, even if the variables are not 
#explicitly declared as arrays.
string=abcABC123ABCabc
echo ${string[@]}               # abcABC123ABCabc
echo ${string[*]}               # abcABC123ABCabc 
echo ${string[0]}               # abcABC123ABCabc
echo ${string[1]}               # No output!
                                # Why?
echo ${#string[@]}              # 1
                                # One element in the array.
                                # The string itself.

#========================================================================
#   -various methods of access/handle element of array
#========================================================================
#array各種存取方法
vaarray=( zero one two three four five )
# Element 0   1   2    3     4    5
echo "\${variable[@]}: (${vaarray[@]})"
echo "\${vaarray[0]}: ${vaarray[0]}"       #  zero
echo "\${vaarray:0}: ${vaarray:0}"        #  zero
                       #  Parameter expansion of first element,
                       #+ starting at position # 0 (1st character).
echo "\${vaarray:1}: ${vaarray:1}"        #  ero
                       #  Parameter expansion of first element,
                       #+ starting at position # 1 (2nd character).

echo "===字串長度/陣列個數==="

echo "\${#vaarray[0]}: ${#vaarray[0]}"      #  4
                       #  Length of first element of vaarray.
echo "\${#vaarray}: ${#vaarray}"         #  4
                       #  Length of first element of vaarray.
                       #  (Alternate notation)

echo "\${#vaarray[1]}: ${#vaarray[1]}"      #  3
                       #  Length of second element of vaarray.
                       #  vaarrays in Bash have zero-based indexing.

echo "\${#vaarray[*]}: ${#vaarray[*]}"      #  6
                       #  Number of elements in vaarray.
echo "\${#vaarray[@]}: ${#vaarray[@]}"      #  6
                       #  Number of elements in vaarray.

echo "另一種宣告與\"的運用"

vaarray2=( [0]="first element" [1]="second element" [3]="fourth element" )
#            ^     ^       ^     ^      ^       ^     ^      ^       ^
# Quoting permits embedding whitespace within individual vaarray elements.

echo "\${vaarray2[0]}: ${vaarray2[0]}"      # first element
echo "\${vaarray2[1]}: ${vaarray2[1]}"      # second element
echo "\${vaarray2[2]}: ${vaarray2[2]}"      #
                       # Skipped in initialization, and therefore null.
echo "\${vaarray2[3]}: ${vaarray2[3]}"      # fourth element
echo "\${#vaarray2[0]}: ${#vaarray2[0]}"     # 13    (length of first element)
echo "\${#vaarray2[*]}: ${#vaarray2[*]}"     # 3     (number of elements in vaarray)

#========================================================================
#   -string operation for array
#========================================================================
echo "===-string operation for array==="
#  In general, any string operation using the ${name ... } notation
#+ can be applied to all string elements in an array,
#+ with the ${name[@] ... } or ${name[*] ...} notation.

arrayStrOp=( one two three four five five )


#========================================================================
# Trailing Substring Extraction
#========================================================================
echo "===Trailing Substring Extraction==="
echo ${arrayStrOp[@]:0}     # one two three four five five
#                ^        All elements.

echo ${arrayStrOp[@]:1}     # two three four five five
#                ^        All elements following element[0].

echo ${arrayStrOp[@]:1:2}   # two three
#                  ^      Only the two elements after element[0].


#========================================================================
# Substring Removal
#========================================================================
echo "===Substring Removal==="

# Removes shortest match from front of string(s).

echo ${arrayStrOp[@]#f*r}   # one two three five five
#               ^       # Applied to all elements of the array.
                        # Matches "four" and removes it.

# Longest match from front of string(s)
echo ${arrayStrOp[@]##t*e}  # one two four five five
#               ^^      # Applied to all elements of the array.
                        # Matches "three" and removes it.

# Shortest match from back of string(s)
echo ${arrayStrOp[@]%h*e}   # one two t four five five
#               ^       # Applied to all elements of the array.
                        # Matches "hree" and removes it.

# Longest match from back of string(s)
echo ${arrayStrOp[@]%%t*e}  # one two four five five
#               ^^      # Applied to all elements of the array.
                        # Matches "three" and removes it.
#========================================================================
# Substring Replacement
#========================================================================
echo "===Substring Replacement==="

# Replace first occurrence of substring with replacement.
echo ${arrayStrOp[@]/fiv/XYZ}   # one two three four XYZe XYZe
#               ^           # Applied to all elements of the array.

# Replace all occurrences of substring.
echo ${arrayStrOp[@]//iv/YY}    # one two three four fYYe fYYe
                            # Applied to all elements of the array.

# Delete all occurrences of substring.
# Not specifing a replacement defaults to 'delete' ...
echo ${arrayStrOp[@]//fi/}      # one two three four ve ve
#               ^^          # Applied to all elements of the array.

# Replace front-end occurrences of substring.
echo ${arrayStrOp[@]/#fi/XY}    # one two three four XYve XYve
#                ^          # Applied to all elements of the array.

# Replace back-end occurrences of substring.
echo ${arrayStrOp[@]/%ve/ZZ}    # one two three four fiZZ fiZZ
#                ^          # Applied to all elements of the array.

echo ${arrayStrOp[@]/%o/XX}     # one twXX three four five five
#                ^          # Why?

echo "-----------------------------"


replacement() {
    echo -n "!!!"
}

echo ${arrayStrOp[@]/%e/$(replacement)}
#                ^  ^^^^^^^^^^^^^^
# on!!! two thre!!! four fiv!!! fiv!!!
# The stdout of replacement() is the replacement string.
# Q.E.D: The replacement action is, in effect, an 'assignment.'

echo "------------------------------------"

#  Accessing the "for-each":
echo ${arrayStrOp[@]//*/$(replacement optional_arguments)}
#                ^^ ^^^^^^^^^^^^^
# !!! !!! !!! !!! !!! !!!

#  Now, if Bash would only pass the matched string
#+ to the function being called . . .

echo


#========================================================================
#   -declare/read -a array (宣告/使用者輸入陣列)
#========================================================================
#  Before reaching for a Big Hammer -- Perl, Python, or all the rest --
#  recall:
#    $( ... ) is command substitution.
#    A function runs as a sub-process.
#    A function writes its output (if echo-ed) to stdout.
#    Assignment, in conjunction with "echo" and command substitution,
#+   can read a function's stdout.
#    The name[@] notation specifies (the equivalent of) a "for-each"
#+   operation.
#  Bash is more powerful than you think!

#宣告colors為indexed array(Each name is an indexed array variable)
#  All subsequent commands in this script will treat
#+ the variable "colors" as an array.
declare -a colors
#使用unset來delete array的元素

echo "Enter your favorite colors (separated from each other by a space)."

#輸入陣列元素
#  Special option to 'read' command,
#+ allowing assignment of elements in an array.
#read -a colors    # Enter at least 3 colors to demonstrate features below.
colors=(blue red green)
echo "\${color[*]}: ${colors[*]}"

element_count=${#colors[@]}
# Special syntax to extract number of elements in array.
#     element_count=${#colors[*]} works also.
#
#  The "@" variable allows word splitting within quotes
#+ (extracts variables separated by whitespace).
#
#  This corresponds to the behavior of "$@" and "$*"
#+ in positional parameters. 

index=0

while [ "$index" -lt "$element_count" ]
do    # List all the elements in the array.
  echo ${colors[$index]}
  #    ${colors[index]} also works because it's within ${ ... } brackets.
  let "index = $index + 1"
  # Or:
  #    ((index++))
done
# Each array element listed on a separate line.
# If this is not desired, use  echo -n "${colors[$index]} "
#
# Doing it with a "for" loop instead:
#   for i in "${colors[@]}"
#   do
#     echo "$i"
#   done
# (Thanks, S.C.)

echo

# Again, list all the elements in the array, but using a more elegant method.
  echo ${colors[@]}          # echo ${colors[*]} also works.

echo

# The "unset" command deletes elements of an array, or entire array.
unset colors[1]              # Remove 2nd element of array.
                             # Same effect as   colors[1]=
echo  ${colors[@]}           # List array again, missing 2nd element.

unset colors                 # Delete entire array.
                             #  unset colors[*] and
                             #+ unset colors[@] also work.
echo; echo -n "Colors gone."			   
echo ${colors[@]}            # List array again, now empty.


#========================================================================
#   -add element/pop element/copy array
#========================================================================
# An empty array is not the same as an array with empty elements.

  array0=( first second third )
  array1=( '' )   # "array1" consists of one empty element.
  array2=( )      # No elements . . . "array2" is empty.
  array3=(   )    # What about this array?


echo
ListArray()
{
echo
echo "Elements in array0:  ${array0[@]}"
echo "Elements in array1:  ${array1[@]}"
echo "Elements in array2:  ${array2[@]}"
echo "Elements in array3:  ${array3[@]}"
echo
echo "Length of first element in array0 = ${#array0}"
echo "Length of first element in array1 = ${#array1}"
echo "Length of first element in array2 = ${#array2}"
echo "Length of first element in array3 = ${#array3}"
echo
echo "Number of elements in array0 = ${#array0[*]}"  # 3
echo "Number of elements in array1 = ${#array1[*]}"  # 1  (Surprise!)
echo "Number of elements in array2 = ${#array2[*]}"  # 0
echo "Number of elements in array3 = ${#array3[*]}"  # 0
}

# ===================================================================

ListArray

# Try extending those arrays.

# ===================================================================
# Adding an element to an array.
# ===================================================================
array0=( "${array0[@]}" "new1" )
array1=( "${array1[@]}" "new1" )
array2=( "${array2[@]}" "new1" )
array3=( "${array3[@]}" "new1" )

ListArray

# or
array0[${#array0[*]}]="new2"
array1[${#array1[*]}]="new2"
array2[${#array2[*]}]="new2"
array3[${#array3[*]}]="new2"

ListArray

# When extended as above, arrays are 'stacks' ...
# Above is the 'push' ...
# The stack 'height' is:
height=${#array2[@]}
echo
echo "Stack height for array2 = $height"

# ===================================================================
# The 'pop' is:
# ===================================================================
unset array2[${#array2[@]}-1]   #  Arrays are zero-based,
height=${#array2[@]}            #+ which means first element has index 0.
echo
echo "POP"
echo "New stack height for array2 = $height"

ListArray

# List only 2nd and 3rd elements of array0.
from=1		    # Zero-based numbering.
to=2
array3=( ${array0[@]:1:2} )
echo
echo "Elements in array3:  ${array3[@]}"

# Works like a string (array of characters).
# Try some other "string" forms.

# Replacement:
array4=( ${array0[@]/second/2nd} )
echo
echo "Elements in array4:  ${array4[@]}"

# Replace all matching wildcarded string.
array5=( ${array0[@]//new?/old} )
echo
echo "Elements in array5:  ${array5[@]}"

# Just when you are getting the feel for this . . .
array6=( ${array0[@]#*new} )
echo # This one might surprise you.
echo "Elements in array6:  ${array6[@]}"

array7=( ${array0[@]#new1} )
echo # After array6 this should not be a surprise.
echo "Elements in array7:  ${array7[@]}"

# Which looks a lot like . . .
array8=( ${array0[@]/new1/} )
echo
echo "Elements in array8:  ${array8[@]}"
#  So what can one say about this?

#========================================================================
#  The string operations are performed on
#+ each of the elements in var[@] in succession.
#  Therefore : Bash supports string vector operations.
#  If the result is a zero length string,
#+ that element disappears in the resulting assignment.
#  However, if the expansion is in quotes, the null elements remain.

#  Michael Zick:    Question, are those strings hard or soft quotes?
#  Nathan Coulter:  There is no such thing as "soft quotes."
#!    What's really happening is that
#!+   the pattern matching happens after
#!+   all the other expansions of [word]
#!+   in cases like ${parameter#word}.
#========================================================================
zap='new*'
array9=( ${array0[@]/$zap/} )
echo
echo "Number of elements in array9:  ${#array9[@]}"
array9=( "${array0[@]/$zap/}" )
echo "Elements in array9:  ${array9[@]}"
# This time the null elements remain.
echo "Number of elements in array9:  ${#array9[@]}"


# Just when you thought you were still in Kansas . . .
array10=( ${array0[@]#$zap} )
echo
echo "Elements in array10:  ${array10[@]}"
# But, the asterisk in zap won't be interpreted if quoted.
array10=( ${array0[@]#"$zap"} )
echo
echo "Elements in array10:  ${array10[@]}"
# Well, maybe we _are_ still in Kansas . . .
# (Revisions to above code block by Nathan Coulter.)


#  Compare array7 with array10.
#  Compare array8 with array9.

#  Reiterating: No such thing as soft quotes!
#  Nathan Coulter explains:
#  Pattern matching of 'word' in ${parameter#word} is done after
#+ parameter expansion and *before* quote removal.
#  In the normal case, pattern matching is done *after* quote removal.

#========================================================================
# Copying an array.
#========================================================================
array2=( "${array1[@]}" )
# or
array2="${array1[@]}"
#
#  However, this fails with "sparse" arrays,
#+ arrays with holes (missing elements) in them,
#+ as Jochen DeSmet points out.
# ------------------------------------------
  Jarray1[0]=0
# Jarray1[1] not assigned
  Jarray1[2]=2
  Jarray2=( "${Jarray1[@]}" )       # Copy it?

echo ${Jarray2[0]}      # 0
echo ${Jarray2[2]}      # (null), should be 2
# ------------------------------------------



# Adding an element to an array.
Jarray1=( "${Jarray1[@]}" "new element" )
echo ${Jarray1[@]}
echo ${Jarray1[2]}
# or
Jarray2[${#Jarray2[*]}]="new element"
echo ${Jarray2[@]}


aaaa=("aa" "bd kdfjl")
echo ${aaaa[1]}

#========================================================================
#   -sort array
#========================================================================
#對bash陣列變數的內容做排序

# 處理對象陣列
array1=('Tokyo' 'Beijing' 'Washington D.C.' 'Longon' 'Paris')

# 將換行文字設定到IFS中，用「${array[*]}」將陣列中的內容
# 逐行輸出
IFS=$'\n'

# 排序之後，將結果顯示
array2=(`echo "${array1[*]}" | sort`)
echo "${array2[*]}"


#========================================================================
#   -compare two array to find the same/the diff
#========================================================================
#求出陣列的差
# 處理對象
array1=('Tokyo' 'Beijing' 'Washington D.C.' 'London' 'Paris')
array2=('Paris' 'Pusan' 'London' 'Kuala Lumpur' 'Tokyo' 'Canberra')

# 將換行字元設定到IFS中，變更區隔文字
IFS=$'\n'

# 首先，求出雙方陣列中所含的內容
both=(`{ echo "${array1[*]}"; echo "${array2[*]}"; } | sort | uniq -d`)

# 從array1將重複的部分去掉，就可以取得array1中有而array2中卻沒有
# 的項目了
only1=(`{ echo "${array1[*]}"; echo "${both[*]}"; } | sort | uniq -u`)

# 顯示結果
echo "${only1[*]}"


#========================================================================
#   -emulate array by variable
#========================================================================
#使用陣列

# array_put
#     將值設定到陣列中
# $1 - 變數名稱
# $2 - 編號(流水號)
# $3 - 設定的值
array_put() {
    eval "$1_$2=\"\$3\""
}

# array_get
#     取出陣列的值
# $1 - 變數名稱
# $2 - 編號(流水號)
array_get() {
    eval "echo \${$1_$2}"
}

# array_count
#     計算陣列的要素數
# $1 - 變數名稱
array_count() {
    set | grep "^$1_[0-9]*=" | wc -l
}

# 將值設定陣列中
array_put sample 0 "zero"
array_put sample 1 "one"
array_put sample 2 "two"

# 顯示各要素的值
for i in 0 1 2; do
    item=`array_get sample $i`
    echo "sample_$i='$item'"
done

# 顯示要素個數
echo "Count: `array_count sample`"

#========================================================================
#========================================================================
